package com.microtf.inscription.framework.form;

import com.microtf.inscription.framework.annotation.FieldAnnotation;
import com.microtf.inscription.framework.annotation.FormAnnotation;
import com.microtf.inscription.framework.vo.BaseVo;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Service;
import org.springframework.util.ReflectionUtils;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 表单生成器
 *
 * @author guliuzhong
 */
@Data
@Slf4j
@Service

public class FormGenerateService implements ApplicationContextAware {
    ApplicationContext applicationContext;

    @Override
    public void setApplicationContext( ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public <T extends BaseVo> Form generalFormData(T classData) {
        return generalForm(null, classData);
    }

    public Form generalForm(Class<? extends BaseVo> classic) {
        return generalForm(classic, null);
    }

    public Form generalForm(Class<? extends BaseVo> classic,Object classDate) {
        Form.FormBuilder builder1 = Form.builder();
        List<FormItem> fromFields = new ArrayList<>();
        Class<?> tmpClass = classic != null ? classic : classDate.getClass();
        FormAnnotation classAnnotation = AnnotationUtils.getAnnotation(tmpClass, FormAnnotation.class);
        if (classAnnotation != null) {
            builder1.title(classAnnotation.title()).hideSubmit(classAnnotation.hideSubmit());
            builder1.message(classAnnotation.message()).method(classAnnotation.method());
        }
        while (tmpClass != null) {
            Field[] declaredFields = tmpClass.getDeclaredFields();
            PropertyDescriptor[] propertyDescriptors = new PropertyDescriptor[0];
            try {
                BeanInfo beanInfo = Introspector.getBeanInfo(tmpClass);
                propertyDescriptors = beanInfo.getPropertyDescriptors();
            } catch (IntrospectionException e) {
                log.warn("获取Bean类属性出错{}",e.getMessage());
            }
            for (Field field : declaredFields) {
                FormItem.FormItemBuilder builder = FormItem.builder();
                builder.fieldName(field.getName());
                builder.className(field.getType().getSimpleName());
                FieldAnnotation fieldAnnotation = AnnotationUtils.getAnnotation(field, FieldAnnotation.class);
                DateTimeFormat dateTimeFormatAnnotation = AnnotationUtils.getAnnotation(field, DateTimeFormat.class);

                if (fieldAnnotation != null) {
                    builder.title(fieldAnnotation.title()).tip(fieldAnnotation.tip());
                    builder.order(fieldAnnotation.order()).hide(fieldAnnotation.hide());
                    builder.element("input");
                    if (fieldAnnotation.largeText()) {
                        builder.element("textarea");
                        builder.htmlEditor(fieldAnnotation.htmlEditor());
                        builder.htmlEditUpload(fieldAnnotation.htmlEditorUpload());
                    }
                    if (field.getType().getTypeName().equalsIgnoreCase(String[].class.getTypeName())
                            || field.getType().getTypeName().equalsIgnoreCase(Integer[].class.getTypeName())
                            || field.getType().getTypeName().equalsIgnoreCase(Long[].class.getTypeName())
                            || field.getType().getTypeName().equalsIgnoreCase(Float[].class.getTypeName())
                            || field.getType().getTypeName().equalsIgnoreCase(Double[].class.getTypeName())
                            || field.getType().getTypeName().equalsIgnoreCase(Character[].class.getTypeName())
                    ) {
                        String[] strings = fieldAnnotation.listData();
                        builder.multiple(fieldAnnotation.multiple()).element("select");
                        if (strings.length > 1) {
                            if (strings.length % 2 == 0) {
                                Set<FormOption> formOptionSet = new HashSet<>();
                                for (int i = 0; i < strings.length; i++) {
                                    FormOption.FormOptionBuilder formOptionBuilder = FormOption.builder();
                                    formOptionBuilder.id(strings[i]).text(strings[i + 1]);
                                    formOptionSet.add(formOptionBuilder.build());
                                    i++;
                                }
                                builder.formOption(formOptionSet);
                            } else {
                                assert classAnnotation != null;
                                log.error("生成表单{}错误未找到列表{}的数据源错误，数据名值需要配对，数量应为偶数", classAnnotation.title(), fieldAnnotation.listData());
                            }
                        } else {
                            String listBeanName = fieldAnnotation.listBeanName();
                            String listMethod = fieldAnnotation.listMethod();
                            if (!listBeanName.isEmpty() && !listMethod.isEmpty()) {
                                Object bean = applicationContext.getBean(listBeanName);
                                Method method = ReflectionUtils.findMethod(bean.getClass(), listMethod);
                                assert method != null;
                                if ("java.util.Set<com.microtf.inscription.framework.form.FormOption>".equalsIgnoreCase(method.getGenericReturnType().getTypeName())) {
                                    @SuppressWarnings("unchecked")
                                    Set<FormOption> formOptions = (Set<FormOption>) ReflectionUtils.invokeMethod(method, bean);
                                    builder.formOption(formOptions);
                                } else {
                                    assert classAnnotation != null;
                                    log.error("生成表单{}错误未找到列表{}的数据源,bean{},method{}", classAnnotation.title(), fieldAnnotation.title(), fieldAnnotation.listBeanName(), fieldAnnotation.listMethod());
                                }
                            }
                        }
                    }
                }
                if (dateTimeFormatAnnotation != null) {
                    builder.element("date");
                    if (dateTimeFormatAnnotation.pattern().contains("HH:mm:ss")) {
                        builder.format("ymdTime");
                    } else {
                        builder.format("ymd");
                    }
                }
                try {
                    if (classDate != null) {
                        Object fieldData=null;
                        Optional<PropertyDescriptor> first = Arrays.stream(propertyDescriptors).filter(item -> item.getName().equals(field.getName())).findFirst();
                        if(first.isPresent()){
                            PropertyDescriptor propertyDescriptor = first.get();
                            Method readMethod = propertyDescriptor.getReadMethod();
                            try {
                                fieldData = readMethod.invoke(classDate);
                            } catch (InvocationTargetException e) {
                                log.error("读取字段{}出错{}",field.getName(),e.getMessage());
                            }
                        }else{
                            log.warn("字段{}不可读取，请检查表单方法",field.getName());
                        }
                        if (fieldData != null) {
                            if (field.getType() == Boolean.class) {
                                Boolean aBoolean = (Boolean) fieldData;
                                if (aBoolean) {
                                    builder.value(boolTrue());
                                } else {
                                    builder.value(boolFalse());
                                }
                            } else if (field.getType() == String.class) {
                                builder.value((String) fieldData);
                            } else if (field.getType() == Integer.class) {
                                builder.value(String.valueOf(fieldData));
                            } else if (field.getType() == Long.class) {
                                builder.value(String.valueOf(fieldData));
                            } else if (field.getType() == Float.class) {
                                builder.value(String.valueOf(fieldData));
                            } else if (field.getType() == Double.class) {
                                builder.value(String.valueOf(fieldData));
                            } else if (field.getType() == Character.class) {
                                builder.value(String.valueOf(fieldData));
                            } else if (field.getType() == Date.class) {
                                Date date = (Date) fieldData;
                                SimpleDateFormat simpleDateFormat;
                                if (dateTimeFormatAnnotation != null) {
                                    simpleDateFormat = new SimpleDateFormat(dateTimeFormatAnnotation.pattern());
                                } else {
                                    simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                                }
                                builder.value(simpleDateFormat.format(date));
                            } else if (field.getType().getTypeName().equalsIgnoreCase(String[].class.getTypeName())) {
                                String[] strings = (String[]) fieldData;
                                List<String> collect = Stream.of(strings).collect(Collectors.toList());
                                builder.listValue(strings).listKey(collect);
                            } else if (field.getType().getTypeName().equalsIgnoreCase(Integer[].class.getTypeName())) {
                                String[] strings = Stream.of((Integer[]) fieldData).map(Object::toString).toArray(String[]::new);
                                List<String> collect = Stream.of(strings).map(String::toString).collect(Collectors.toList());
                                builder.listValue(strings).listKey(collect);
                            } else if (field.getType().getTypeName().equalsIgnoreCase(Long[].class.getTypeName())) {
                                String[] strings = Stream.of((Long[]) fieldData).map(Object::toString).toArray(String[]::new);
                                List<String> collect = Stream.of(strings).collect(Collectors.toList());
                                builder.listValue(strings).listKey(collect);
                            } else if (field.getType().getTypeName().equalsIgnoreCase(Float[].class.getTypeName())) {
                                String[] strings = Stream.of((Float[]) fieldData).map(Object::toString).toArray(String[]::new);
                                List<String> collect = Stream.of(strings).collect(Collectors.toList());
                                builder.listValue(strings).listKey(collect);
                            } else if (field.getType().getTypeName().equalsIgnoreCase(Double[].class.getTypeName())) {
                                String[] strings = Stream.of((Double[]) fieldData).map(Object::toString).toArray(String[]::new);
                                List<String> collect = Stream.of(strings).collect(Collectors.toList());
                                builder.listValue(strings).listKey(collect);
                            } else if (field.getType().getTypeName().equalsIgnoreCase(Character[].class.getTypeName())) {
                                String[] strings = Stream.of((Character[]) fieldData).map(Object::toString).toArray(String[]::new);
                                List<String> collect = Stream.of(strings).collect(Collectors.toList());
                                builder.listValue(strings).listKey(collect);
                            } else if (field.getType() == Byte.class) {
                                throw new IllegalAccessException("不能处理Byte类型");
                            }
                        }
                    }
                } catch (IllegalAccessException   e) {
                    e.printStackTrace();
                    log.warn("获取表单数据值出错{},{}", field.getName(), e.getMessage());
                }
                fromFields.add(builder.build());
            }
            if (tmpClass == BaseVo.class) {
                break;
            }
            tmpClass = tmpClass.getSuperclass();
        }
        Collections.sort(fromFields);
        builder1.formItems(fromFields);
        return builder1.build();
    }

    public Boolean value2Boolean(String value) {
        String stringTrue = "TRUE";
        String yes = "yes";
        String num1 = "1";
        String y = "Y";
        return yes.equalsIgnoreCase(value) || y.equalsIgnoreCase(value) || stringTrue.equalsIgnoreCase(value) || num1.equalsIgnoreCase(value);
    }

    public String boolTrue() {
        return "TRUE";
    }

    public String boolFalse() {
        return "FALSE";
    }
}
